﻿#include "enemy.h"

Enemy_Manager emanager;

void Enemy::Render(CL_GraphicContext &gc)
{
	appearance->Render(gc, this);
}

void Enemy::RenderDead(CL_GraphicContext &gc)
{
	alive = appearance->RenderDead(gc, this);
}

void EnemyBoss::RenderBack(CL_GraphicContext &gc, float time)
{
	((Boss_Appearance*)appearance)->RenderBack(gc, this, time);
}

void EnemyBoss::RenderFront(CL_GraphicContext &gc, float time)
{
	((Boss_Appearance*)appearance)->RenderFront(gc, this, time);
}

void Enemy::Update(float time)
{
	prev_position = position;
	alive = path->Update(this, time);
}

// Далее играть в зависимости от типа врага
void Enemy::PlayDeadSound()
{
	if (is_boss)
		smanager.PlaySound("enemy_dead2");
	else
		smanager.PlaySound("enemy_dead1");
}

void Enemy::ProcessPatterns()
{
	for (size_t t=0; t < (size_t)shots; t++)
	{
		if (bullet_patterns[t].launch_type)
		{
			switch (bullet_patterns[t].launch_type)
			{
			case 1:
				// Запуск по времени
				if (time_lived >= bullet_patterns[t].launch)
				{
					bullet_patterns[t].launch_type = 0;
					patterns.LaunchPattern(position, bullet_patterns[t].type, this);
				}
				break;
			case 2:
				// Запуск по расстоянию
				if (traveled >= bullet_patterns[t].launch)
				{
					bullet_patterns[t].launch_type = 0;
					patterns.LaunchPattern(position, bullet_patterns[t].type, this);
				}
				break;
			case 3:
				// Запуск по триггеру
				if (bullet_patterns[t].launch == trigger)
				{
					bullet_patterns[t].launch_type = 0;
					patterns.LaunchPattern(position, bullet_patterns[t].type, this);
				}
				break;
			}
		}
	}
}


Enemy_Manager::Enemy_Manager():
active_enemies_count(0)
{
	enemies.reserve(200);
	dead_enemies.reserve(200);
	active_enemies_count = 0;
	next_id = 0;
}

Enemy_Manager::~Enemy_Manager()
{
	enemies.clear();
}

int Enemy_Manager::Create(Enemy &p_enemy_description)
{
	CL_SharedPtr<Enemy> new_enemy(new Enemy);
	*new_enemy = p_enemy_description;
	new_enemy->alive = 1;
	new_enemy->dead = 0;
	new_enemy->prev_position = new_enemy->position;
	new_enemy->time_lived = 0;
	new_enemy->traveled = 0;
	new_enemy->trigger = 0;
	unsigned int n_id=next_id++;
	new_enemy->id = n_id;
	enemies.push_back(new_enemy);
	active_enemies_count++;
	cl_log_event("Enemies", "Create new enemy %1 , enemies count : %2", n_id, enemies.size());
	cl_log_event("Enemies", "Active enemies count : %1", active_enemies_count);
	return n_id;
}

int Enemy_Manager::Create(EnemyBoss &p_enemy_description)
{
	CL_SharedPtr<Enemy> new_enemy(new EnemyBoss);
	*(static_cast<EnemyBoss*>(new_enemy.get())) = p_enemy_description;
	new_enemy->alive = 1;
	new_enemy->dead = 0;
	new_enemy->prev_position = new_enemy->position;
	new_enemy->time_lived = 0;
	new_enemy->traveled = 0;
	new_enemy->trigger = 0;
	unsigned int n_id=next_id++;
	new_enemy->id = n_id;
	enemies.push_back(new_enemy);
	active_enemies_count++;
	cl_log_event("Enemies", "Create new boss %1 , enemies count : %2", n_id, enemies.size());
	cl_log_event("Enemies", "Active enemies count : %1", active_enemies_count);
	return n_id;
}

void Enemy_Manager::Render(CL_GraphicContext &gc, CL_Rect &clip)
{
	gc.set_cliprect(clip);
	for(unsigned int t=0; t<active_enemies_count; ++t)
		enemies[t]->Render(gc);
	gc.pop_cliprect();
}

void Enemy_Manager::RenderDead(CL_GraphicContext &gc, CL_Rect &clip)
{
	gc.set_cliprect(clip);
	for(unsigned int t=0; t<dead_enemies.size(); ++t)
		dead_enemies[t]->RenderDead(gc);
	gc.pop_cliprect();
}

void Enemy_Manager::Clear()
{
	active_enemies_count = 0;
	next_id = 0;
	enemies.clear();
}

void Enemy_Manager::Update(float time)
{
	size_t t;
	// Обновляем
	for (t=0;t<active_enemies_count;++t)
	{
		enemies[t]->time_lived += time;
		enemies[t]->time_from_last_frame += time;
		enemies[t]->Update(time);
		if (!enemies[t]->is_boss)
			enemies[t]->ProcessPatterns();
	}
	t = 0;
	// Удаляем ушедших за экран
	while (t < enemies.size())
		if (enemies[t]->alive || (enemies[t]->is_boss && enemies[t]->dead))
			t++;
		else
		{
			enemies.erase(enemies.begin() + t);
			active_enemies_count--;
		}
}

void Enemy_Manager::UpdateDead(float time)
{
	size_t t;
	// Обновляем
	for (t=0;t<dead_enemies.size();++t)
	{
		// Просто прибавляем время
		// Для умерших конец жизни определяет рендер
		dead_enemies[t]->time_lived += time;
		dead_enemies[t]->time_from_last_frame += time;
	}
	t = 0;
	// Отработавших остаточную анимацию просто удаляем
	while (t < dead_enemies.size())
		if (dead_enemies[t]->alive)
			t++;
		else
		{
			cl_log_event("Debug", "Deleting dead enemy %1 from stack", dead_enemies[t]->id);
			dead_enemies.erase(dead_enemies.begin() + t);
			cl_log_event("Debug", "Dead enemies remaining : %1", dead_enemies.size());
		}
}

void Enemy_Manager::CalculateCollisions()
{
	size_t t;
	int damage;
	// Обновляем
	for (t=0;t<active_enemies_count;++t)
	{
		damage = bmanager.CollideEnemy(enemies[t].get());
		// Начисляем очки за попадания
		player.score += damage;
		if (enemies[t]->is_boss)
		{
			// Попали в босса
			EnemyBoss* eboss = static_cast<EnemyBoss*>(enemies[t].get());
			eboss->spell_hp -= damage;
		}
		else
		{
			// Обычные феи
			enemies[t]->hp -= damage;
			if (enemies[t]->hp <= 0)
			{
				enemies[t]->alive = 0;
				// Начисляем очки за сбитого
				player.score += enemies[t]->score;
				player.wind += player.wind_kill_gain;
				if (player.wind > 100.0f)
					player.wind = 100.0f;
			}
		}
	}
	t = 0;
	// Переносим уничтоженных в могильник
	while (t < enemies.size())
		if (enemies[t]->alive)
			t++;
		else
		{
			if (enemies[t]->bonus_drop)
			{
				// Выкидываем бонус из тушки
				LaunchBonus(enemies[t].get());
			}
			enemies[t]->alive = 1;
			enemies[t]->dead = 1;
			enemies[t]->animation_frame = 0;
			enemies[t]->time_from_last_frame = 0;
			enemies[t]->time_lived = 0;
			enemies[t]->score = 0; // теперь используем это как счётчик выкинутых частиц
			// Отмотаем позицию назад, чтобы при смерти не было скачка вперёд, когда произойдёт обработка в том же кадре
			enemies[t]->position = enemies[t]->prev_position;
			// Играем звук смерти
			enemies[t]->PlayDeadSound();
			// В могильник
			if (enemies[t]->id > 500)
				cl_log_event("Debug", "Gotcha!");
			dead_enemies.push_back(enemies[t]);
			cl_log_event("Debug", "Bullet collision : put dead enemy %1 to stack", enemies[t]->id);
			cl_log_event("Debug", "Dead enemies remaining : %1", dead_enemies.size());
			// А из живых удаляем
			enemies.erase(enemies.begin() + t);
			active_enemies_count--;
		}
}

void Enemy_Manager::DestroyEnemies(GameObject *canceler)
{
	size_t t;
	// Обновляем
	for (t=0;t<active_enemies_count;++t)
	{
		if (!enemies[t]->is_boss && enemies[t]->Collide(canceler))
			enemies[t]->alive = 0;
	}
	t = 0;
	// Переносим уничтоженных в могильник
	while (t < enemies.size())
		if (enemies[t]->alive)
			t++;
		else
		{
			if (enemies[t]->bonus_drop)
			{
				// Выкидываем бонус из тушки
				LaunchBonus(enemies[t].get());
			}
			enemies[t]->alive = 1;
			enemies[t]->dead = 1;
			enemies[t]->animation_frame = 0;
			enemies[t]->time_from_last_frame = 0;
			enemies[t]->time_lived = 0;
			enemies[t]->score = 0; // теперь используем это как счётчик выкинутых частиц
			// Отмотаем позицию назад, чтобы при смерти не было скачка вперёд, когда произойдёт обработка в том же кадре
			enemies[t]->position = enemies[t]->prev_position;
			// Играем звук смерти
			enemies[t]->PlayDeadSound();
			// В могильник
			dead_enemies.push_back(enemies[t]);
			cl_log_event("Debug", "Destroyer collision : put dead enemy %1 to stack", enemies[t]->id);
			cl_log_event("Debug", "Dead enemies remaining : %1", dead_enemies.size());
			// А из живых удаляем
			enemies.erase(enemies.begin() + t);
			active_enemies_count--;
		}
}

void Enemy_Manager::BombEnemies(float time)
{
	size_t t;
	float damage = player.bomb_damage * time;
	// Обновляем
	for (t=0;t<active_enemies_count;++t)
	{
		// Начисляем очки за дамаг
		player.score += damage;
		if (enemies[t]->is_boss)
		{
			// Попали в босса
			EnemyBoss* eboss = static_cast<EnemyBoss*>(enemies[t].get());
			eboss->spell_hp -= damage;
		}
		else
		{
			// Обычные феи
			enemies[t]->hp -= damage;
			if (enemies[t]->hp <= 0)
			{
				enemies[t]->alive = 0;
				// Начисляем очки за сбитого
				player.score += enemies[t]->score;
				player.wind += player.wind_kill_gain;
				if (player.wind > 100.0f)
					player.wind = 100.0f;
			}
		}
	}
	t = 0;
	// Переносим уничтоженных в могильник
	while (t < enemies.size())
		if (enemies[t]->alive)
			t++;
		else
		{
			if (enemies[t]->bonus_drop)
			{
				// Выкидываем бонус из тушки
				LaunchBonus(enemies[t].get());
			}
			enemies[t]->alive = 1;
			enemies[t]->dead = 1;
			enemies[t]->animation_frame = 0;
			enemies[t]->time_from_last_frame = 0;
			enemies[t]->time_lived = 0;
			enemies[t]->score = 0; // теперь используем это как счётчик выкинутых частиц
			// Отмотаем позицию назад, чтобы при смерти не было скачка вперёд, когда произойдёт обработка в том же кадре
			enemies[t]->position = enemies[t]->prev_position;
			// Играем звук смерти
			enemies[t]->PlayDeadSound();
			// В могильник
			dead_enemies.push_back(enemies[t]);
			// А из живых удаляем
			enemies.erase(enemies.begin() + t);
			active_enemies_count--;
		}
}

int Enemy_Manager::CollidePlayer()
{
	size_t t;
	unsigned int total_damage = 0;
	// Обновляем
	for (t=0;t<active_enemies_count;++t)
	{
		if (enemies[t]->Collide(&player))
		{
			total_damage += enemies[t]->hp;
			if (!enemies[t]->is_boss)
			{
				enemies[t]->alive = 0;
				// Начисляем очки за сбитого, пусть даже своей тушкой
				player.score += enemies[t]->score;
			}
		}
	}
	t = 0;
	// Переносим уничтоженных в могильник
	while (t < enemies.size())
		if (enemies[t]->alive)
			t++;
		else
		{
			if (enemies[t]->bonus_drop)
			{
				// Выкидываем бонус из тушки
				Bonus t_bonus;
				t_bonus.position = enemies[t]->position;
				t_bonus.prev_position = enemies[t]->position;
				t_bonus.speed = -0.11f;
				switch (enemies[t]->bonus_drop)
				{
				case 1:
					// очки
					t_bonus.bonus_type = 1;
					bonus_manager.Add(t_bonus);
					break;
				case 2:
					// паверап
					t_bonus.bonus_type = 0;
					bonus_manager.Add(t_bonus);
					break;
				case 3:
					// время
					t_bonus.bonus_type = 2;
					bonus_manager.Add(t_bonus);
					break;
				case 4:
					// Большой паверап
					t_bonus.bonus_type = 3;
					bonus_manager.Add(t_bonus);
					break;
				}
			}
			enemies[t]->alive = 1;
			enemies[t]->dead = 1;
			enemies[t]->animation_frame = 0;
			enemies[t]->time_from_last_frame = 0;
			enemies[t]->time_lived = 0;
			enemies[t]->score = 0; // теперь используем это как счётчик выкинутых частиц
			// Отмотаем позицию назад, чтобы при смерти не было скачка вперёд, когда произойдёт обработка в том же кадре
			enemies[t]->position = enemies[t]->prev_position;
			// Играем звук смерти
			enemies[t]->PlayDeadSound();
			// В могильник
			dead_enemies.push_back(enemies[t]);
			cl_log_event("Debug", "Player collision : put dead enemy %1 to stack", enemies[t]->id);
			cl_log_event("Debug", "Dead enemies remaining : %1", dead_enemies.size());
			// А из живых удаляем
			enemies.erase(enemies.begin() + t);
			active_enemies_count--;
		}
	return total_damage;
}

CL_Vec2<float> Enemy_Manager::GetClosestEnemyPosition()
{
	if (!active_enemies_count)
		return CL_Vec2<float>(0, 0);
	CL_Vec2<float> t_p = enemies[0]->position;
	float t_len = (enemies[0]->position.x - player.position.x)*(enemies[0]->position.x - player.position.x) + (enemies[0]->position.y - player.position.y)*(enemies[0]->position.y - player.position.y);
	for (size_t t=1;t<active_enemies_count;++t)
	{
		if (((enemies[t]->position.x - player.position.x)*(enemies[t]->position.x - player.position.x) + (enemies[t]->position.y - player.position.y)*(enemies[t]->position.y - player.position.y)) < t_len)
		{
			t_len = (enemies[t]->position.x - player.position.x)*(enemies[t]->position.x - player.position.x) + (enemies[t]->position.y - player.position.y)*(enemies[t]->position.y - player.position.y);
			t_p = enemies[t]->position;
		}
	}
	return t_p;
}

EnemyBoss* Enemy_Manager::GetBoss()
{
	for (size_t t=0;t<active_enemies_count;++t)
	{
		if (enemies[t]->is_boss)
			return static_cast<EnemyBoss*>(enemies[t].get());
	}
	return 0;
}

void Enemy_Manager::LaunchBonus(Enemy* e)
{
	Bonus t_bonus;
	t_bonus.position = e->position;
	t_bonus.prev_position = e->position;
	t_bonus.speed = -0.11f;
	t_bonus.move = CL_Vec2f(0, -0.11f);
	switch (e->bonus_drop)
	{
	case 1:
		// очки
		t_bonus.bonus_type = 1;
		break;
	case 2:
		// паверап
		t_bonus.bonus_type = 0;
		break;
	case 3:
		// время
		t_bonus.bonus_type = 2;
		break;
	case 4:
		// Большой паверап
		t_bonus.bonus_type = 3;
		break;
	}
	bonus_manager.Add(t_bonus);
}
